package hr.fer.pus.dll_will.cr;

import hr.fer.pus.dll_will.cr.actions.GetCRCertificate;
import hr.fer.pus.dll_will.cr.actions.ListFiles;
import hr.fer.pus.dll_will.cr.actions.ListSPs;
import hr.fer.pus.dll_will.cr.actions.RegisterFiles;
import hr.fer.pus.dll_will.cr.actions.RegisterSP;
import hr.fer.pus.dll_will.cr.actions.SimplePrint;
import hr.fer.pus.dll_will.cr.data.FileDescriptor;
import hr.fer.pus.dll_will.sp.SPDescriptor;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.KeyPair;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Repository which has the knowledge of the other repositories content.
 */
public class CentralRepository extends HttpServlet {
	private static final long serialVersionUID = 1L;

	/** Map of servlet actions: K:action name, V:Concrete action. */
	private Map<String, IActionHandler> actions;
	
	/** Map of service provider private/public keys: K:spID, V:sp KeyPair */
	private Map<String, KeyPair> spKeyPairs;
	
	/** List of all files on all service providers. */
	private List<FileDescriptor> files = new LinkedList<FileDescriptor>();
	
	/** List of all SP - s */
	private List<SPDescriptor> serviceproviders = new LinkedList<SPDescriptor>();
	
	/** Private RSA key. */
	private String privateKey;
	
	/** Public RSA key. */
	private String publicKey;

	/**
	 * HTTP GET request processing.
	 */
	@Override
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		processRequest(request, response);
	}

	/**
	 * HTTP POST request processing.
	 */
	@Override
	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		processRequest(request, response);
	}

	/**
	 * Request processing.
	 * 
	 * @param request HTTP Request with parameters.
	 * @param response HTTP Response for forwaring of request.
	 * @throws IOException If read/write error occurs.
	 */
	private void processRequest(HttpServletRequest request,
			HttpServletResponse response) throws IOException {

		ServletOutputStream out = response.getOutputStream();

		response.setContentType("text/plain");
		
		String action = (String) request.getParameter("action");
		System.out.println("action: " + action );
		if (action == null || "".equals(action)) {
			writeErrorMsg(out, "action is not defined!");
			
		} else {
			IActionHandler aHandle = actions.get(action.toLowerCase());
			if (aHandle == null) {
				writeErrorMsg(out, "action " + action + " does not exist!");
			} else {
				aHandle.performAction(out, request);
			}
		}
		
		out.flush();
		out.close();
	}
	
	/**
	 * Writes error message to servlet output stream.
	 * 
	 * @param out
	 * @param msg Error message.
	 * @throws IOException
	 */
	private void writeErrorMsg(ServletOutputStream out, String msg) throws IOException {
		out.write("Error: ".getBytes());
		out.write(msg.getBytes());
	}

	/**
	 * Servlet init. Filling of necessary collections and reading of parameters
	 * from web.xml file.
	 * Parameters:
	 * <ul>
	 *  <li>Public key: public_key_path</li>
	 *  <li>Private key: private_key_path</li>
	 * </ul>
	 */
	@Override
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		
		this.spKeyPairs = new HashMap<String, KeyPair>();
		this.serviceproviders = new LinkedList<SPDescriptor>();
		
		// Reading parameters from web.xml.
		this.privateKey = config.getInitParameter("private_key");
		this.publicKey = config.getInitParameter("public_key");
		
		// Inicialization of servlet actions.
		initActions();
	}
	
	/**
	 * Read {@code file} content.
	 * @param file File path.
	 * @return Content of a {@code file}.
	 * @throws IOException
	 */
	public String readFile(String file) throws IOException {
		BufferedReader reader = null;
		StringBuilder sb = new StringBuilder();

		reader = new BufferedReader(new InputStreamReader(
				new FileInputStream(file)));
		String line = null;

		while (true) {
			line = reader.readLine();
			if (line == null) {
				break;
			}
			sb.append(line).append('\n');
		}
		reader.close();

		return sb.toString();
	}
	
	

	/**
	 * Initializes servlet actions.
	 */
	private void initActions() {
		this.actions = new HashMap<String, IActionHandler>();
		
		this.actions.put("write", new SimplePrint());
		this.actions.put("getpublickey", new GetCRCertificate(publicKey));
		this.actions.put("registersp", new RegisterSP(privateKey, spKeyPairs, serviceproviders));
		this.actions.put("listfiles", new ListFiles(files));
		this.actions.put("listsps", new ListSPs(serviceproviders));
		this.actions.put("registerfiles", new RegisterFiles(files));
		
	}
}
